/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "node/lottie_listener.h"
#include "common/common.h"

namespace LottieTurbo {

static const std::unordered_map<LottieEventType, std::string> lottieEventMap = {
    {LottieEventType::ON_DRAW_FRAME, "onDrawFrame"},       {LottieEventType::ON_ENTER_FRAME, "onEnterFrame"},
    {LottieEventType::ON_LOOP_COMPLETE, "onLoopComplete"}, {LottieEventType::ON_COMPLETE, "onComplete"},
    {LottieEventType::ON_SEGMENT_START, "onSegmentStart"}, {LottieEventType::ON_DESTROY, "onDestroy"},
    {LottieEventType::ON_CONFIG_READY, "onConfigReady"},   {LottieEventType::ON_DATA_READY, "onDataReady"},
    {LottieEventType::ON_DOM_LOADED, "onDOMLoaded"},       {LottieEventType::ON_ERROR, "onError"},
    {LottieEventType::ON_DATA_FAILED, "onDataFailed"},     {LottieEventType::ON_LOADED_IMAGES, "onLoadedImages"}};

LottieListener::LottieListener(napi_env env) : napiHelper_(NapiHelper(env))
{
}

LottieListener::~LottieListener()
{
    funcMap_.clear();
    for (auto &pair : callbackRefs_) {
        this->DeleteCallbackReference(pair.second);
    }
    callbackRefs_.clear();
    napiHelper_.DeleteReference(jsThisRef_);
}

void LottieListener::InitListenerCallback(napi_value listenerJsObject)
{
    // 确保 jsObject 是个对象
    if (!napiHelper_.CheckIsType(listenerJsObject, napi_object)) {
        return;
    }

    jsThisRef_ = napiHelper_.CreateReference(listenerJsObject);

    for (auto &pair : lottieEventMap) {
        this->RegisterSelfCallback(listenerJsObject, pair.first);
    }
}

void LottieListener::RegisterSelfCallback(napi_value listenerJsObject, LottieEventType eventType)
{
    auto it = lottieEventMap.find(eventType);
    if (it == lottieEventMap.end()) {
        // 非法事件类型
        return;
    }

    const std::string &funcName = it->second;
    if (!napiHelper_.CheckFuncIsExist(listenerJsObject, funcName)) {
        return;
    }
    auto jsFunc = napiHelper_.GetObjectProperty(listenerJsObject, funcName);
    this->RegisterCallback(jsFunc, eventType);
}

void LottieListener::RegisterCallback(napi_value callbackFunc, LottieEventType eventType)
{
    if (lottieEventMap.find(eventType) == lottieEventMap.end()) {
        // 非法事件类型
        return;
    }
    // 先删除旧的回调引用，避免内存泄漏
    if (callbackRefs_.find(eventType) != callbackRefs_.end()) {
        napiHelper_.DeleteReference(callbackRefs_[eventType]);
    }
    // 保存回调引用，延长生命周期
    callbackRefs_[eventType] = napiHelper_.CreateReference(callbackFunc);

    // 注册回调
    funcMap_[eventType] = [this, eventType]() {
        // 判断回调引用是否存在
        auto it = callbackRefs_.find(eventType);
        if (it == callbackRefs_.end()) {
            return;
        }
        auto jsFunc = napiHelper_.GetReferenceValue(it->second);
        auto jsThis = napiHelper_.GetReferenceValue(jsThisRef_);
        napiHelper_.Call(jsThis, jsFunc, 0, nullptr);
    };
}

void LottieListener::UnRegisterCallback(LottieEventType eventName)
{
    auto it = funcMap_.find(eventName);
    if (it != funcMap_.end()) {
        funcMap_.erase(it);

        auto refIt = callbackRefs_.find(eventName);
        if (refIt != callbackRefs_.end()) {
            napiHelper_.DeleteReference(refIt->second);
            callbackRefs_.erase(refIt);
        }
    }
}

void LottieListener::TriggerEvent(LottieEventType eventType)
{
    auto it = funcMap_.find(eventType);
    if (it != funcMap_.end()) {
        it->second();
    }
}

void LottieListener::DeleteCallbackReference(napi_ref &callbackRef)
{
    if (callbackRef != nullptr) {
        napiHelper_.DeleteReference(callbackRef);
        callbackRef = nullptr;
    }
}

} // namespace LottieTurbo
